package com.gitee.chili.leisure;

import com.gitee.chili.leisure.exception.BusinessException;
import com.gitee.chili.leisure.game.RoomDetail;
import com.gitee.chili.leisure.util.functional.Function;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 房间缓存管理器
 *
 * @author week solider
 * @since 2020-07-29
 */
@Slf4j
public class RoomManager {

    /**
     * 缓存实例
     */
    private static final Map<Long, RoomDetail> roomInfoMaps = new ConcurrentHashMap<>(256);


    private RoomManager() {
    }

    /**
     * 缓存房间信息
     *
     * @param roomNum  房间号
     * @param roomDetail 房间信息
     */
    public static void storage(Long roomNum, RoomDetail roomDetail) {
        roomInfoMaps.put(roomNum, roomDetail);
    }

    /**
     * 获取房间信息
     *
     * @param roomNum 房间号
     */
    public static RoomDetail obtain(Long roomNum) {
        if (hasRoomNum(roomNum)) {
            return roomInfoMaps.get(roomNum);
        }
        BusinessException exception = new BusinessException("获取房间失败，房间号:" + roomNum);
        log.error("获取房间 {} 信息失败, 为空", roomNum, exception);
        throw exception;
    }

    /**
     * 游戏结束，移除房间信息
     */
    public static void end(Long roomNum) {
        roomInfoMaps.remove(roomNum);
    }

    /**
     * 进入房间
     * <p>
     * 由于类型的关系，这里不能直接进行添加，采用策略模式，但不能保证进入了房间 (可靠性不高)
     *
     * @param roomNum  房间号
     * @param function 执行函数
     */
    public static void entry(Long roomNum, Function<RoomDetail> function) {
        if (hasRoomNum(roomNum)) {
            RoomDetail info = obtain(roomNum);
            function.apply(info);
        }
        String msg = String.format("进入房间失败，房间号: %s", roomNum);
        throw new BusinessException(msg);
    }

    /**
     * 退出房间
     *
     * @param roomNum  房间号
     * @param memberId 玩家ID
     */
    public static void exit(Long roomNum, Long memberId) {
        if(!hasRoomNum(roomNum))
            return;
        RoomDetail roomDetail = obtain(roomNum);
        if (Objects.isNull(roomDetail)) {
            String msg = String.format("退出房间失败，房间已解散 房间号: [ %s ] ，玩家Id: [ %s ]", roomNum, memberId);
            BusinessException exception = new BusinessException(msg);
            log.error(msg, exception);
            throw exception;
        }
        roomDetail.getPlayers().removeIf(p -> p.getUserId().equals(memberId));
    }

    /**
     * 房间是否过期
     *
     * @param roomNum 房间号
     */
    public static boolean hasRoomNum(Long roomNum) {
        return roomInfoMaps.containsKey(roomNum);
    }

}
